/*
 * src/interfaces/ecpg/pgtypeslib/timestamp.c
 */
#include "postgres_fe.h"

#include <time.h>
#include <limits.h>
#include <math.h>

#ifdef __FAST_MATH__
#error -ffast-math is known to break this code
#endif

#include "dt.h"
#include "pgtypes_date.h"
#include "pgtypes_timestamp.h"
#include "pgtypeslib_extern.h"

static int64 fc_time2t(const int fc_hour, const int fc_min, const int fc_sec, const fsec_t fc_fsec)
{
	return (((((fc_hour * MINS_PER_HOUR) + fc_min) * SECS_PER_MINUTE) + fc_sec) * USECS_PER_SEC) + fc_fsec;
}								/* time2t() */

static timestamp
fc_dt2local(timestamp fc_dt, int fc_tz)
{
	fc_dt -= (fc_tz * USECS_PER_SEC);
	return fc_dt;
}								/* dt2local() */

/* tm2timestamp()
 * 将 tm 结构转换为时间戳数据类型。
 * 请注意，年份是 _非_ 以 1900 为基准，而是一个明确的完整值。
 * 同样，月份是以 1 为基准的，而非以 0 为基准。
 *
 * 失败时返回 -1（溢出）。
 */
int tm2timestamp(struct tm *fc_tm, fsec_t fc_fsec, int *fc_tzp, timestamp * fc_result)
{
	int			fc_dDate;
	int64		fc_time;

	/* 防止 Julian 日 routines 溢出 */
	if (!IS_VALID_JULIAN(fc_tm->tm_year, fc_tm->tm_mon, tm->tm_mday))
		return -1;

	fc_dDate = date2j(fc_tm->tm_year, fc_tm->tm_mon, fc_tm->tm_mday) - date2j(2000, 1, 1);
	fc_time = fc_time2t(fc_tm->tm_hour, fc_tm->tm_min, fc_tm->tm_sec, fc_fsec);
	*fc_result = (fc_dDate * USECS_PER_DAY) + fc_time;
	/* 检查主要溢出 */
	if ((*fc_result - fc_time) / USECS_PER_DAY != fc_dDate)
		return -1;
	/* 检查是否刚好溢出（除了时间的环绕是可以的） */
	/* 注意：我们希望允许 1999-12-31 24:00:00 */
	if ((*fc_result < 0 && fc_dDate > 0) ||
		(*fc_result > 0 && fc_dDate < -1))
		return -1;
	if (fc_tzp != NULL)
		*fc_result = fc_dt2local(*fc_result, -(*fc_tzp));

	/* 最终范围检查捕获刚好超出范围的时间戳 */
	if (!IS_VALID_TIMESTAMP(*fc_result))
		return -1;

	return 0;
}								/* tm2timestamp() */

static timestamp
fc_SetEpochTimestamp(void)
{
	int64		fc_noresult = 0;
	timestamp	fc_dt;
	struct tm	fc_tt,
			   *fc_tm = &fc_tt;

	if (GetEpochTime(fc_tm) < 0)
		return fc_noresult;

	tm2timestamp(fc_tm, 0, NULL, &fc_dt);
	return fc_dt;
}								/* SetEpochTimestamp() */

/* timestamp2tm()
 * 将时间戳数据类型转换为 POSIX 时间结构。
 * 请注意，年份是 _非_ 以 1900 为基准，而是一个明确的完整值。
 * 同样，月份是以 1 为基准的，而非以 0 为基准。
 * 返回：
 *	 0 表示成功
 *	-1 表示超出范围
 *
 * 对于在系统支持的 time_t 范围内的日期，转换为
 *	本地时区。如果超出该范围，保持为 GMT。- tgl 97/05/27
 */
static int fc_timestamp2tm(timestamp fc_dt, int *fc_tzp, struct tm *fc_tm, fsec_t *fc_fsec, const char **fc_tzn)
{
	int64		fc_dDate,
				fc_date0;
	int64		fc_time;
#if defined(HAVE_STRUCT_TM_TM_ZONE) || defined(HAVE_INT_TIMEZONE)
	time_t		fc_utime;
	struct tm  *fc_tx;
#endif

	fc_date0 = date2j(2000, 1, 1);

	fc_time = fc_dt;
	TMODULO(fc_time, fc_dDate, USECS_PER_DAY);

	if (fc_time < INT64CONST(0))
	{
		fc_time += USECS_PER_DAY;
		fc_dDate -= 1;
	}

	/* 添加偏移量以从 J2000 返回到标准 Julian 日期 */
	fc_dDate += fc_date0;

	/* Julian 日 routine 不适用于负 Julian 日 */
	if (fc_dDate < 0 || fc_dDate > (timestamp) INT_MAX)
		return -1;

	j2date((int) fc_dDate, &fc_tm->tm_year, &fc_tm->tm_mon, &fc_tm->tm_mday);
	dt2time(fc_time, &fc_tm->tm_hour, &fc_tm->tm_min, &fc_tm->tm_sec, fc_fsec);

	if (fc_tzp != NULL)
	{
		/*
		 * 这是否在 localtime() 接口的能力范围内？
		 * 那么使用它来旋转到本地时区。
		 */
		if (IS_VALID_UTIME(fc_tm->tm_year, fc_tm->tm_mon, fc_tm->tm_mday))
		{
#if defined(HAVE_STRUCT_TM_TM_ZONE) || defined(HAVE_INT_TIMEZONE)

			fc_utime = fc_dt / USECS_PER_SEC +
				((fc_date0 - date2j(1970, 1, 1)) * INT64CONST(86400));

			fc_tx = localtime(&fc_utime);
			fc_tm->tm_year = fc_tx->tm_year + 1900;
			fc_tm->tm_mon = fc_tx->tm_mon + 1;
			fc_tm->tm_mday = fc_tx->tm_mday;
			fc_tm->tm_hour = fc_tx->tm_hour;
			fc_tm->tm_min = fc_tx->tm_min;
			fc_tm->tm_isdst = fc_tx->tm_isdst;

#if defined(HAVE_STRUCT_TM_TM_ZONE)
			fc_tm->tm_gmtoff = fc_tx->tm_gmtoff;
			fc_tm->tm_zone = fc_tx->tm_zone;

			*fc_tzp = -fc_tm->tm_gmtoff;	/* DecodeDate()
 * 解码包含分隔符的日期字符串。
 * 确保字段的完整性。
 */
			if (fc_tzn != NULL)
				*fc_tzn = fc_tm->tm_zone;
#elif defined(HAVE_INT_TIMEZONE)
			*tzp = (tm->tm_isdst > 0) ? TIMEZONE_GLOBAL - SECS_PER_HOUR : TIMEZONE_GLOBAL;
			if (tzn != NULL)
				*tzn = TZNAME_GLOBAL[(tm->tm_isdst > 0)];
#endif
#else							/* 不允许字段过多 */
			*tzp = 0;
			/* 将其标记为 *没有* 可用时区 */
			tm->tm_isdst = -1;
			if (tzn != NULL)
				*tzn = NULL;
#endif
		}
		else
		{
			*fc_tzp = 0;
			/* 将其标记为 *没有* 可用时区 */
			fc_tm->tm_isdst = -1;
			if (fc_tzn != NULL)
				*fc_tzn = NULL;
		}
	}
	else
	{
		fc_tm->tm_isdst = -1;
		if (fc_tzn != NULL)
			*fc_tzn = NULL;
	}

	fc_tm->tm_yday = fc_dDate - date2j(fc_tm->tm_year, 1, 1) + 1;

	return 0;
}								/* timestamp2tm() */

/* EncodeSpecialTimestamp()
 *	* 将保留的时间戳数据类型转换为字符串。
 *	 */
static void fc_EncodeSpecialTimestamp(timestamp fc_dt, char *fc_str)
{
	if (TIMESTAMP_IS_NOBEGIN(fc_dt))
		strcpy(fc_str, EARLY);
	else if (TIMESTAMP_IS_NOEND(fc_dt))
		strcpy(fc_str, LATE);
	else
		abort();				/* 不应该发生 */
}

timestamp
PGTYPEStimestamp_from_asc(char *fc_str, char **fc_endptr)
{
	timestamp	fc_result;
	int64		fc_noresult = 0;
	fsec_t		fc_fsec;
	struct tm	fc_tt,
			   *fc_tm = &fc_tt;
	int			fc_dtype;
	int			fc_nf;
	char	   *fc_field[MAXDATEFIELDS];
	int			fc_ftype[MAXDATEFIELDS];
	char		fc_lowstr[MAXDATELEN + MAXDATEFIELDS];
	char	   *fc_realptr;
	char	  **fc_ptr = (fc_endptr != NULL) ? fc_endptr : &fc_realptr;

	if (strlen(fc_str) > MAXDATELEN)
	{
		errno = PGTYPES_TS_BAD_TIMESTAMP;
		return fc_noresult;
	}

	if (ParseDateTime(fc_str, fc_lowstr, fc_field, fc_ftype, &fc_nf, fc_ptr) != 0 ||
		DecodeDateTime(fc_field, fc_ftype, fc_nf, &fc_dtype, fc_tm, &fc_fsec, 0) != 0)
	{
		errno = PGTYPES_TS_BAD_TIMESTAMP;
		return fc_noresult;
	}

	switch (fc_dtype)
	{
		case DTK_DATE:
			if (tm2timestamp(fc_tm, fc_fsec, NULL, &fc_result) != 0)
			{
				errno = PGTYPES_TS_BAD_TIMESTAMP;
				return fc_noresult;
			}
			break;

		case DTK_EPOCH:
			fc_result = fc_SetEpochTimestamp();
			break;

		case DTK_LATE:
			TIMESTAMP_NOEND(fc_result);
			break;

		case DTK_EARLY:
			TIMESTAMP_NOBEGIN(fc_result);
			break;

		default:
			errno = PGTYPES_TS_BAD_TIMESTAMP;
			return fc_noresult;
	}

	/* 调整时间戳以适应 Typmod(&result, typmod); */

	/*
	 * 由于很难测试 noresult，确保如果没有
	 * 错误发生，errno 为 0。
	 */
	errno = 0;
	return fc_result;
}

char * PGTYPEStimestamp_to_asc(timestamp fc_tstamp)
{
	struct tm	fc_tt,
			   *fc_tm = &fc_tt;
	char		fc_buf[MAXDATELEN + 1];
	fsec_t		fc_fsec;
	int			fc_DateStyle = 1;	/* 这默认为 USE_ISO_DATES，我们应该
								 * 让它成为一个选项吗？ */

	if (TIMESTAMP_NOT_FINITE(fc_tstamp))
		fc_EncodeSpecialTimestamp(fc_tstamp, fc_buf);
	else if (fc_timestamp2tm(fc_tstamp, NULL, fc_tm, &fc_fsec, NULL) == 0)
		EncodeDateTime(fc_tm, fc_fsec, false, 0, NULL, fc_DateStyle, fc_buf, 0);
	else
	{
		errno = PGTYPES_TS_BAD_TIMESTAMP;
		return NULL;
	}
	return pgtypes_strdup(fc_buf);
}

void PGTYPEStimestamp_current(timestamp * fc_ts)
{
	struct tm	fc_tm;

	GetCurrentDateTime(&fc_tm);
	if (errno == 0)
		tm2timestamp(&fc_tm, 0, NULL, fc_ts);
}

static int fc_dttofmtasc_replace(timestamp * fc_ts, date fc_dDate, int fc_dow, struct tm *fc_tm,
				   char *fc_output, int *fc_pstr_len, const char *fc_fmtstr)
{
	union un_fmt_comb fc_replace_val;
	int			fc_replace_type;
	int			fc_i;
	const char *fc_p = fc_fmtstr;
	char	   *fc_q = fc_output;

	while (*fc_p)
	{
		if (*fc_p == '%')
		{
			fc_p++;
			/* 修复编译器警告 */
			fc_replace_type = PGTYPES_TYPE_NOTHING;
			switch (*fc_p)
			{
					/* 一周中日期的缩写名称 */
					/* XXX 应该考虑区域设置 */
				case 'a':
					fc_replace_val.str_val = pgtypes_date_weekdays_short[fc_dow];
					fc_replace_type = PGTYPES_TYPE_STRING_CONSTANT;
					break;
					/* 一周中日期的全名 */
					/* XXX 应该考虑区域设置 */
				case 'A':
					fc_replace_val.str_val = days[fc_dow];
					fc_replace_type = PGTYPES_TYPE_STRING_CONSTANT;
					break;
					/* 月份的缩写名称 */
					/* XXX 应该考虑区域设置 */
				case 'b':
				case 'h':
					fc_replace_val.str_val = months[fc_tm->tm_mon - 1];
					fc_replace_type = PGTYPES_TYPE_STRING_CONSTANT;
					break;
					/* 月份的全名 */
					/* XXX 应该考虑区域设置 */
				case 'B':
					fc_replace_val.str_val = pgtypes_date_months[fc_tm->tm_mon - 1];
					fc_replace_type = PGTYPES_TYPE_STRING_CONSTANT;
					break;

					/*
					 * 当前区域设置的首选日期和时间表示法。
					 */
				case 'c':
					/* XXX */
					break;
					/* 带前导零的世纪数 */
				case 'C':
					fc_replace_val.uint_val = fc_tm->tm_year / 100;
					fc_replace_type = PGTYPES_TYPE_UINT_2_LZ;
					break;
					/* 带前导零的天数（01 - 31） */
				case 'd':
					fc_replace_val.uint_val = fc_tm->tm_mday;
					fc_replace_type = PGTYPES_TYPE_UINT_2_LZ;
					break;
					/* 日期格式为 mm/dd/yy */
				case 'D':

					/*
					 * ts, dDate, dow, tm 是有关时间戳的信息
					 *
					 * q 是当前输出缓冲区的开始
					 *
					 * pstr_len 是一个指向剩余输出大小的指针，
					 * 即 q 的大小
					 */
					fc_i = fc_dttofmtasc_replace(fc_ts, fc_dDate, fc_dow, fc_tm,
										   fc_q, fc_pstr_len,
										   "%m/%d/%y");
					if (fc_i)
						return fc_i;
					break;
					/* 带前导空格的天数（01 - 31） */
				case 'e':
					fc_replace_val.uint_val = fc_tm->tm_mday;
					fc_replace_type = PGTYPES_TYPE_UINT_2_LS;
					break;

					/*
					 * 替代格式修饰符
					 */
				case 'E':
					{
						char		fc_tmp[4] = "%Ex";

						fc_p++;
						if (*fc_p == '\0')
							return -1;
						fc_tmp[2] = *fc_p;

						/*
						 * strftime 的月份是 0 基础的，我们的是 1 基础的
						 */
						fc_tm->tm_mon -= 1;
						fc_i = strftime(fc_q, *fc_pstr_len, fc_tmp, fc_tm);
						if (fc_i == 0)
							return -1;
						while (*fc_q)
						{
							fc_q++;
							(*fc_pstr_len)--;
						}
						fc_tm->tm_mon += 1;
						fc_replace_type = PGTYPES_TYPE_NOTHING;
						break;
					}

					/*
					 * ISO 8601年，以十进制数字表示的世纪。对应于ISO周数的4位数字年份。
					 */
				case 'G':
					{
						/* 保持编译器安静 - 不要使用文字格式 */
						const char *fc_fmt = "%G";

						fc_tm->tm_mon -= 1;
						fc_i = strftime(fc_q, *fc_pstr_len, fc_fmt, fc_tm);
						if (fc_i == 0)
							return -1;
						while (*fc_q)
						{
							fc_q++;
							(*fc_pstr_len)--;
						}
						fc_tm->tm_mon += 1;
						fc_replace_type = PGTYPES_TYPE_NOTHING;
					}
					break;

					/*
					 * 类似于%G，但不包括世纪，即使用2位数字年份（00-99）。
					 */
				case 'g':
					{
						const char *fc_fmt = "%g"; /* 让编译器安静关于2位数字年份 */

						fc_tm->tm_mon -= 1;
						fc_i = strftime(fc_q, *fc_pstr_len, fc_fmt, fc_tm);
						if (fc_i == 0)
							return -1;
						while (*fc_q)
						{
							fc_q++;
							(*fc_pstr_len)--;
						}
						fc_tm->tm_mon += 1;
						fc_replace_type = PGTYPES_TYPE_NOTHING;
					}
					break;
					/* 小时（24小时制），前导零 */
				case 'H':
					fc_replace_val.uint_val = fc_tm->tm_hour;
					fc_replace_type = PGTYPES_TYPE_UINT_2_LZ;
					break;
					/* 小时（12小时制），前导零 */
				case 'I':
					fc_replace_val.uint_val = fc_tm->tm_hour % 12;
					fc_replace_type = PGTYPES_TYPE_UINT_2_LZ;
					break;

					/*
					 * 一年的天数，以十进制数字表示，前导零。范围从001到366。
					 */
				case 'j':
					fc_replace_val.uint_val = fc_tm->tm_yday;
					fc_replace_type = PGTYPES_TYPE_UINT_3_LZ;
					break;

					/*
					 * 小时（24小时制）。前导零将被转换为空格。
					 */
				case 'k':
					fc_replace_val.uint_val = fc_tm->tm_hour;
					fc_replace_type = PGTYPES_TYPE_UINT_2_LS;
					break;

					/*
					 * 小时（12小时制）。前导零将被转换为空格。
					 */
				case 'l':
					fc_replace_val.uint_val = fc_tm->tm_hour % 12;
					fc_replace_type = PGTYPES_TYPE_UINT_2_LS;
					break;
					/* 月份，以前导零的十进制数字表示 */
				case 'm':
					fc_replace_val.uint_val = fc_tm->tm_mon;
					fc_replace_type = PGTYPES_TYPE_UINT_2_LZ;
					break;
					/* 分钟，以前导零的十进制数字表示 */
				case 'M':
					fc_replace_val.uint_val = fc_tm->tm_min;
					fc_replace_type = PGTYPES_TYPE_UINT_2_LZ;
					break;
					/* 换行符 */
				case 'n':
					fc_replace_val.char_val = '\n';
					fc_replace_type = PGTYPES_TYPE_CHAR;
					break;
					/* AM/PM指示符（大写） */
					/* XXX 应该考虑区域设置 */
				case 'p':
					if (fc_tm->tm_hour < 12)
						fc_replace_val.str_val = "AM";
					else
						fc_replace_val.str_val = "PM";
					fc_replace_type = PGTYPES_TYPE_STRING_CONSTANT;
					break;
					/* AM/PM指示符（小写） */
					/* XXX 应该考虑区域设置 */
				case 'P':
					if (fc_tm->tm_hour < 12)
						fc_replace_val.str_val = "am";
					else
						fc_replace_val.str_val = "pm";
					fc_replace_type = PGTYPES_TYPE_STRING_CONSTANT;
					break;
					/* 时间格式为%I:%M:%S %p */
					/* XXX 应该考虑区域设置 */
				case 'r':
					fc_i = fc_dttofmtasc_replace(fc_ts, fc_dDate, fc_dow, fc_tm,
										   fc_q, fc_pstr_len,
										   "%I:%M:%S %p");
					if (fc_i)
						return fc_i;
					break;
					/* 24小时表示法的时间 (%H:%M) */
				case 'R':
					fc_i = fc_dttofmtasc_replace(fc_ts, fc_dDate, fc_dow, fc_tm,
										   fc_q, fc_pstr_len,
										   "%H:%M");
					if (fc_i)
						return fc_i;
					break;
					/* 自Epoch（1970-01-01）以来的秒数 */
				case 's':
					fc_replace_val.int64_val = (*fc_ts - fc_SetEpochTimestamp()) / 1000000.0;
					fc_replace_type = PGTYPES_TYPE_INT64;
					break;
					/* 秒，以前导零的十进制数字表示 */
				case 'S':
					fc_replace_val.uint_val = fc_tm->tm_sec;
					fc_replace_type = PGTYPES_TYPE_UINT_2_LZ;
					break;
					/* 一个制表符 */
				case 't':
					fc_replace_val.char_val = '\t';
					fc_replace_type = PGTYPES_TYPE_CHAR;
					break;
					/* 24小时表示法的时间 (%H:%M:%S) */
				case 'T':
					fc_i = fc_dttofmtasc_replace(fc_ts, fc_dDate, fc_dow, fc_tm,
										   fc_q, fc_pstr_len,
										   "%H:%M:%S");
					if (fc_i)
						return fc_i;
					break;

					/*
					 * 星期几，以十进制表示，星期一=1，星期天=7
					 */
				case 'u':
					fc_replace_val.uint_val = fc_dow;
					if (fc_replace_val.uint_val == 0)
						fc_replace_val.uint_val = 7;
					fc_replace_type = PGTYPES_TYPE_UINT;
					break;
					/* 一年的周数，以十进制数字表示 */
				case 'U':
					fc_tm->tm_mon -= 1;
					fc_i = strftime(fc_q, *fc_pstr_len, "%U", fc_tm);
					if (fc_i == 0)
						return -1;
					while (*fc_q)
					{
						fc_q++;
						(*fc_pstr_len)--;
					}
					fc_tm->tm_mon += 1;
					fc_replace_type = PGTYPES_TYPE_NOTHING;
					break;

					/*
					 * 当前年份的ISO 8601:1988周数，以十进制数字表示。
					 */
				case 'V':
					{
						/* 保持编译器安静 - 不要使用文字格式 */
						const char *fc_fmt = "%V";

						fc_i = strftime(fc_q, *fc_pstr_len, fc_fmt, fc_tm);
						if (fc_i == 0)
							return -1;
						while (*fc_q)
						{
							fc_q++;
							(*fc_pstr_len)--;
						}
						fc_replace_type = PGTYPES_TYPE_NOTHING;
					}
					break;

					/*
					 * 星期几，以十进制表示，星期天为0，星期一为1。
					 */
				case 'w':
					fc_replace_val.uint_val = fc_dow;
					fc_replace_type = PGTYPES_TYPE_UINT;
					break;
					/* 一年的周数（另一种定义） */
				case 'W':
					fc_tm->tm_mon -= 1;
					fc_i = strftime(fc_q, *fc_pstr_len, "%U", fc_tm);
					if (fc_i == 0)
						return -1;
					while (*fc_q)
					{
						fc_q++;
						(*fc_pstr_len)--;
					}
					fc_tm->tm_mon += 1;
					fc_replace_type = PGTYPES_TYPE_NOTHING;
					break;

					/*
					 * 当前地区首选的日期表示，不包含时间。
					 */
				case 'x':
					{
						const char *fc_fmt = "%x"; /* 让编译器安静关于2位数字年份 */

						fc_tm->tm_mon -= 1;
						fc_i = strftime(fc_q, *fc_pstr_len, fc_fmt, fc_tm);
						if (fc_i == 0)
							return -1;
						while (*fc_q)
						{
							fc_q++;
							(*fc_pstr_len)--;
						}
						fc_tm->tm_mon += 1;
						fc_replace_type = PGTYPES_TYPE_NOTHING;
					}
					break;

					/*
					 * 当前地区首选的时间表示，不包含日期。
					 */
				case 'X':
					fc_tm->tm_mon -= 1;
					fc_i = strftime(fc_q, *fc_pstr_len, "%X", fc_tm);
					if (fc_i == 0)
						return -1;
					while (*fc_q)
					{
						fc_q++;
						(*fc_pstr_len)--;
					}
					fc_tm->tm_mon += 1;
					fc_replace_type = PGTYPES_TYPE_NOTHING;
					break;
					/* 不包括世纪的年份（2位，前导零） */
				case 'y':
					fc_replace_val.uint_val = fc_tm->tm_year % 100;
					fc_replace_type = PGTYPES_TYPE_UINT_2_LZ;
					break;
					/* 包括世纪的年份（4位） */
				case 'Y':
					fc_replace_val.uint_val = fc_tm->tm_year;
					fc_replace_type = PGTYPES_TYPE_UINT;
					break;
					/* 与GMT的时区偏移 */
				case 'z':
					fc_tm->tm_mon -= 1;
					fc_i = strftime(fc_q, *fc_pstr_len, "%z", fc_tm);
					if (fc_i == 0)
						return -1;
					while (*fc_q)
					{
						fc_q++;
						(*fc_pstr_len)--;
					}
					fc_tm->tm_mon += 1;
					fc_replace_type = PGTYPES_TYPE_NOTHING;
					break;
					/* 时区的名称或缩写 */
				case 'Z':
					fc_tm->tm_mon -= 1;
					fc_i = strftime(fc_q, *fc_pstr_len, "%Z", fc_tm);
					if (fc_i == 0)
						return -1;
					while (*fc_q)
					{
						fc_q++;
						(*fc_pstr_len)--;
					}
					fc_tm->tm_mon += 1;
					fc_replace_type = PGTYPES_TYPE_NOTHING;
					break;
					/* 一个%符号 */
				case '%':
					fc_replace_val.char_val = '%';
					fc_replace_type = PGTYPES_TYPE_CHAR;
					break;
				case '\0':
					/* fmtstr: foo%' - 字符串以%符号结束 */

					/*
					 * 这不符合规范
					 */
					return -1;
				default:

					/*
					 * 如果我们不知道模式，我们只是复制它
					 */
					if (*fc_pstr_len > 1)
					{
						*fc_q = '%';
						fc_q++;
						(*fc_pstr_len)--;
						if (*fc_pstr_len > 1)
						{
							*fc_q = *fc_p;
							fc_q++;
							(*fc_pstr_len)--;
						}
						else
						{
							*fc_q = '\0';
							return -1;
						}
						*fc_q = '\0';
					}
					else
						return -1;
					break;
			}
			fc_i = pgtypes_fmt_replace(fc_replace_val, fc_replace_type, &fc_q, fc_pstr_len);
			if (fc_i)
				return fc_i;
		}
		else
		{
			if (*fc_pstr_len > 1)
			{
				*fc_q = *fc_p;
				(*fc_pstr_len)--;
				fc_q++;
				*fc_q = '\0';
			}
			else
				return -1;
		}
		fc_p++;
	}
	return 0;
}


int PGTYPEStimestamp_fmt_asc(timestamp * fc_ts, char *fc_output, int fc_str_len, const char *fc_fmtstr)
{
	struct tm	fc_tm;
	fsec_t		fc_fsec;
	date		fc_dDate;
	int			fc_dow;

	fc_dDate = PGTYPESdate_from_timestamp(*fc_ts);
	fc_dow = PGTYPESdate_dayofweek(fc_dDate);
	fc_timestamp2tm(*fc_ts, NULL, &fc_tm, &fc_fsec, NULL);

	return fc_dttofmtasc_replace(fc_ts, fc_dDate, fc_dow, &fc_tm, fc_output, &fc_str_len, fc_fmtstr);
}

int PGTYPEStimestamp_sub(timestamp * fc_ts1, timestamp * fc_ts2, interval * fc_iv)
{
	if (TIMESTAMP_NOT_FINITE(*fc_ts1) || TIMESTAMP_NOT_FINITE(*fc_ts2))
		return PGTYPES_TS_ERR_EINFTIME;
	else
		fc_iv->time = (*fc_ts1 - *fc_ts2);

	fc_iv->month = 0;

	return 0;
}

int PGTYPEStimestamp_defmt_asc(const char *fc_str, const char *fc_fmt, timestamp * fc_d)
{
	int			fc_year,
				fc_month,
				fc_day;
	int			fc_hour,
				fc_minute,
				fc_second;
	int			fc_tz;

	int			fc_i;
	char	   *fc_mstr;
	char	   *fc_mfmt;

	if (!fc_fmt)
		fc_fmt = "%Y-%m-%d %H:%M:%S";
	if (!fc_fmt[0])
		return 1;

	fc_mstr = pgtypes_strdup(fc_str);
	fc_mfmt = pgtypes_strdup(fc_fmt);

	/*
	 * 初始化为不可能的值，以便我们可以看到字段是否被指定
	 */
	/* XXX与公元前1年有关的歧义？ */
	fc_year = -1;
	fc_month = -1;
	fc_day = -1;
	fc_hour = 0;
	fc_minute = -1;
	fc_second = -1;
	fc_tz = 0;

	fc_i = PGTYPEStimestamp_defmt_scan(&fc_mstr, fc_mfmt, fc_d, &fc_year, &fc_month, &fc_day, &fc_hour, &fc_minute, &fc_second, &fc_tz);
	free(fc_mstr);
	free(fc_mfmt);
	return fc_i;
}

/*
* 向时间戳添加一个间隔
*
*	*tout = tin + span
*
*	 成功时返回0
*	 失败时返回-1
*
*/

int PGTYPEStimestamp_add_interval(timestamp * fc_tin, interval * fc_span, timestamp * fc_tout)
{
	if (TIMESTAMP_NOT_FINITE(*fc_tin))
		*fc_tout = *fc_tin;
	else
	{
		if (fc_span->month != 0)
		{
			struct tm	fc_tt,
					   *fc_tm = &fc_tt;
			fsec_t		fc_fsec;

			if (fc_timestamp2tm(*fc_tin, NULL, fc_tm, &fc_fsec, NULL) != 0)
				return -1;
			fc_tm->tm_mon += fc_span->month;
			if (fc_tm->tm_mon > MONTHS_PER_YEAR)
			{
				fc_tm->tm_year += (fc_tm->tm_mon - 1) / MONTHS_PER_YEAR;
				fc_tm->tm_mon = (fc_tm->tm_mon - 1) % MONTHS_PER_YEAR + 1;
			}
			else if (fc_tm->tm_mon < 1)
			{
				fc_tm->tm_year += fc_tm->tm_mon / MONTHS_PER_YEAR - 1;
				fc_tm->tm_mon = fc_tm->tm_mon % MONTHS_PER_YEAR + MONTHS_PER_YEAR;
			}


			/* 调整月底边界问题... */
			if (fc_tm->tm_mday > day_tab[isleap(fc_tm->tm_year)][fc_tm->tm_mon - 1])
				fc_tm->tm_mday = (day_tab[isleap(fc_tm->tm_year)][fc_tm->tm_mon - 1]);


			if (tm2timestamp(fc_tm, fc_fsec, NULL, fc_tin) != 0)
				return -1;
		}

		*fc_tin += fc_span->time;
		*fc_tout = *fc_tin;
	}

	return 0;
}


/*
* 从时间戳中减去一个时间间隔
*
*	*tout = tin - span
*
*	 成功时返回0
*	 失败时返回-1
*
*/

int PGTYPEStimestamp_sub_interval(timestamp * fc_tin, interval * fc_span, timestamp * fc_tout)
{
	interval	fc_tspan;

	fc_tspan.month = -fc_span->month;
	fc_tspan.time = -fc_span->time;

	return PGTYPEStimestamp_add_interval(fc_tin, &fc_tspan, fc_tout);
}
